﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Xml;
using GoToTags.Nfc;
using GoToTags.Nfc.Ndef;
using GoToTags.Nfc.Reader;
using GoToTags.Nfc.Records;
using GoToTags.Nfc.Util;
using GoToTags.Nfc.Models;

namespace Touch2Like.Tags
{
    public class Utility
    {
        public const string LicenseKey = "lgAAAF6+ktIUoc0BMQBOYW1lPUxlcmFuIExJdSNDb21wYW55PSNFbWFpbD1sZXJhbi5saXVAZ21haWwuY29th6JwJB0yaSVQIl5QapcDEzfUqkjXStNNI4ZXTB7H+ACdQD6RsUoZmLv5EqG/O8Kg";

        public const string WCFUsername = "Touch2Like";
        public const string WCFPassword = "$passw0rd1$";

        public static string GetPriceString(object o)
        {
            if (o == null) return "0.00";
            string x = o.ToString();
            if (x.Contains("."))
            {
                string[] xx = x.Split('.');
                xx[1] = xx[1] + "000";
                if (xx[1].Length > 2) xx[1] = xx[1].Substring(0, 2);
                return xx[0] + "." + xx[1];
            }
            else
            {
                if (string.IsNullOrEmpty(x)) return "0.00";
                return x + ".00";
            }
        }

        public static string GetNodeContentByXPath(XmlNode parentNode, string xpath, XmlNamespaceManager xnm)
        {
            string data = "";
            if (parentNode != null)
            {
                XmlNode node = parentNode.SelectSingleNode(xpath, xnm);
                if (node != null)
                {
                    data = node.InnerText.Trim();
                }
            }

            return data;
        }

        public static int? GetInteger(XmlNode node)
        {
            int? value = null;
            if (node != null && node.InnerText.Trim() != "" && IsInteger(node.InnerText.Trim()))
            {
                value = Convert.ToInt32(node.InnerText.Trim());
            }

            return value;
        }

        public static DateTime? GetDateTime(XmlNode node)
        {
            DateTime? value = null;
            if (node != null && node.InnerText.Trim() != "")
            {
                string[] datas = node.InnerText.Trim().Split('T');
                if (datas[1].IndexOf(".") != -1)
                {
                    datas[1] = datas[1].Substring(0, datas[1].IndexOf(".") + 1);
                }

                string strDate = datas[0] + " " + datas[1];
                value = Convert.ToDateTime(strDate);
            }

            return value;
        }

        public static string GetString(XmlNode node)
        {
            if (node != null)
            {
                return node.InnerText.Trim();
            }
            else
            {
                return null;
            }
        }

        public static bool IsInteger(string value)
        {
            return Regex.IsMatch(value, @"^\d*$");
        }

        public static bool IsNumber(string value)
        {
            return Regex.IsMatch(value, @"^\d*[.]?\d*$");
        }

        public static void AppendXmlAttribute(XmlDocument doc, XmlNode node, string attrName, string attrValue)
        {
            XmlAttribute attr = doc.CreateAttribute(attrName);
            attr.Value = attrValue;
            node.Attributes.Append(attr);
        }

        public static XmlNode AddXmlChildNode(XmlDocument doc, XmlNode node, string nodeName, string nodeValue)
        {
            XmlNode nodeChild = doc.CreateElement(nodeName);
            nodeChild.InnerText = nodeValue;

            node.AppendChild(nodeChild);
            return nodeChild;
        }

        public static XmlNode AppendXmlNode(XmlDocument doc, string nodeName)
        {
            XmlNode node = doc.CreateElement(nodeName);
            doc.AppendChild(node);

            return node;
        }

        public static XmlNode AppendXmlNode(XmlDocument xmlDoc, XmlNode parentNode, string nodeName)
        {
            XmlElement node = xmlDoc.CreateElement(nodeName);
            if (parentNode != null)
            {
                parentNode.AppendChild(node);
            }

            return node;
        }

        public static XmlNode AppendXmlNode(XmlDocument xmlDoc, XmlNode parentNode, string nodeName, string text)
        {
            if (text == "")
                return null;

            XmlNode node = xmlDoc.CreateElement(nodeName);

            node.InnerText = text;
            if (parentNode != null)
            {
                parentNode.AppendChild(node);
            }

            return node;
        }

        public static XmlNode AppendXmlNode(XmlDocument xmlDoc, XmlNode parentNode, string nodeName, string attrName, string value)
        {
            XmlElement node = xmlDoc.CreateElement(nodeName);
            node.SetAttribute(attrName, value);
            if (parentNode != null)
            {
                parentNode.AppendChild(node);
            }

            return node;
        }

        public static class Encryption
        {
            /// <summary>
            /// Encrypt clear text
            /// </summary>
            /// <param name="clearText">Clear text</param>
            /// <returns>Encrypted value</returns>
            public static string Encrypt(string clearText)
            {
                return RijndaelSimple.Encrypt(clearText, RijndaelSimple.passPhrase,
                    RijndaelSimple.saltValue, RijndaelSimple.hashAlgorithm, RijndaelSimple.passwordIterations,
                    RijndaelSimple.initVector, RijndaelSimple.keySize);
            }

            /// <summary>
            /// Decrypt encrypted text
            /// </summary>
            /// <param name="encryptedText">Encrypted text</param>
            /// <returns>Clear value</returns>
            public static string Decrypt(string encryptedText)
            {
                try
                {
                    return RijndaelSimple.Decrypt(encryptedText, RijndaelSimple.passPhrase,
                        RijndaelSimple.saltValue, RijndaelSimple.hashAlgorithm, RijndaelSimple.passwordIterations,
                        RijndaelSimple.initVector, RijndaelSimple.keySize);
                }
                catch
                {
                    encryptedText = encryptedText.Replace(' ', '+');

                    return RijndaelSimple.Decrypt(encryptedText, RijndaelSimple.passPhrase,
                        RijndaelSimple.saltValue, RijndaelSimple.hashAlgorithm, RijndaelSimple.passwordIterations,
                        RijndaelSimple.initVector, RijndaelSimple.keySize);
                }
            }
        }

        internal static class RijndaelSimple
        {
            internal const string passPhrase = "T0uch2L1keas@5pr@seTP"; // can be any string
            internal const string saltValue = "XIs@1tValue";    // can be any string
            internal const string hashAlgorithm = "SHA1";             // can be "MD5"
            internal const int passwordIterations = 2;                 // can be any number
            internal const string initVector = "@1B2c3D4e5F6g7TP";// must be 16 bytes
            internal const int keySize = 128;               // can be 192 or 256
            /// <summary>
            /// Encrypts specified plaintext using Rijndael symmetric key algorithm
            /// and returns a base64-encoded result.
            /// </summary>
            /// <param name="plainText">
            /// Plaintext value to be encrypted.
            /// </param>
            /// <param name="passPhrase">
            /// Passphrase from which a pseudo-random password will be derived. The
            /// derived password will be used to generate the encryption key.
            /// Passphrase can be any string. In this example we assume that this
            /// passphrase is an ASCII string.
            /// </param>
            /// <param name="saltValue">
            /// Salt value used along with passphrase to generate password. Salt can
            /// be any string. In this example we assume that salt is an ASCII string.
            /// </param>
            /// <param name="hashAlgorithm">
            /// Hash algorithm used to generate password. Allowed values are: "MD5" and
            /// "SHA1". SHA1 hashes are a bit slower, but more secure than MD5 hashes.
            /// </param>
            /// <param name="passwordIterations">
            /// Number of iterations used to generate password. One or two iterations
            /// should be enough.
            /// </param>
            /// <param name="initVector">
            /// Initialization vector (or IV). This value is required to encrypt the
            /// first block of plaintext data. For RijndaelManaged class IV must be 
            /// exactly 16 ASCII characters long.
            /// </param>
            /// <param name="keySize">
            /// Size of encryption key in bits. Allowed values are: 128, 192, and 256. 
            /// Longer keys are more secure than shorter keys.
            /// </param>
            /// <returns>
            /// Encrypted value formatted as a base64-encoded string.
            /// </returns>
            public static string Encrypt(string plainText,
                string passPhrase,
                string saltValue,
                string hashAlgorithm,
                int passwordIterations,
                string initVector,
                int keySize)
            {
                // Convert strings into byte arrays.
                // Let us assume that strings only contain ASCII codes.
                // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
                // encoding.
                byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
                byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

                // Convert our plaintext into a byte array.
                // Let us assume that plaintext contains UTF8-encoded characters.
                byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

                // First, we must create a password, from which the key will be derived.
                // This password will be generated from the specified passphrase and 
                // salt value. The password will be created using the specified hash 
                // algorithm. Password creation can be done in several iterations.
                PasswordDeriveBytes password = new PasswordDeriveBytes(
                    passPhrase,
                    saltValueBytes,
                    hashAlgorithm,
                    passwordIterations);

                // Use the password to generate pseudo-random bytes for the encryption
                // key. Specify the size of the key in bytes (instead of bits).
                byte[] keyBytes = password.GetBytes(keySize / 8);

                // Create uninitialized Rijndael encryption object.
                RijndaelManaged symmetricKey = new RijndaelManaged();

                // It is reasonable to set encryption mode to Cipher Block Chaining
                // (CBC). Use default options for other symmetric key parameters.
                symmetricKey.Mode = CipherMode.CBC;

                // Generate encryptor from the existing key bytes and initialization 
                // vector. Key size will be defined based on the number of the key 
                // bytes.
                ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                    keyBytes,
                    initVectorBytes);

                // Define memory stream which will be used to hold encrypted data.
                MemoryStream memoryStream = new MemoryStream();

                // Define cryptographic stream (always use Write mode for encryption).
                CryptoStream cryptoStream = new CryptoStream(memoryStream,
                    encryptor,
                    CryptoStreamMode.Write);
                // Start encrypting.
                cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

                // Finish encrypting.
                cryptoStream.FlushFinalBlock();

                // Convert our encrypted data from a memory stream into a byte array.
                byte[] cipherTextBytes = memoryStream.ToArray();

                // Close both streams.
                memoryStream.Close();
                cryptoStream.Close();

                // Convert encrypted data into a base64-encoded string.
                string cipherText = Convert.ToBase64String(cipherTextBytes);

                // Return encrypted string.
                return cipherText;
            }

            /// <summary>
            /// Decrypts specified ciphertext using Rijndael symmetric key algorithm.
            /// </summary>
            /// <param name="cipherText">
            /// Base64-formatted ciphertext value.
            /// </param>
            /// <param name="passPhrase">
            /// Passphrase from which a pseudo-random password will be derived. The
            /// derived password will be used to generate the encryption key.
            /// Passphrase can be any string. In this example we assume that this
            /// passphrase is an ASCII string.
            /// </param>
            /// <param name="saltValue">
            /// Salt value used along with passphrase to generate password. Salt can
            /// be any string. In this example we assume that salt is an ASCII string.
            /// </param>
            /// <param name="hashAlgorithm">
            /// Hash algorithm used to generate password. Allowed values are: "MD5" and
            /// "SHA1". SHA1 hashes are a bit slower, but more secure than MD5 hashes.
            /// </param>
            /// <param name="passwordIterations">
            /// Number of iterations used to generate password. One or two iterations
            /// should be enough.
            /// </param>
            /// <param name="initVector">
            /// Initialization vector (or IV). This value is required to encrypt the
            /// first block of plaintext data. For RijndaelManaged class IV must be
            /// exactly 16 ASCII characters long.
            /// </param>
            /// <param name="keySize">
            /// Size of encryption key in bits. Allowed values are: 128, 192, and 256.
            /// Longer keys are more secure than shorter keys.
            /// </param>
            /// <returns>
            /// Decrypted string value.
            /// </returns>
            /// <remarks>
            /// Most of the logic in this function is similar to the Encrypt
            /// logic. In order for decryption to work, all parameters of this function
            /// - except cipherText value - must match the corresponding parameters of
            /// the Encrypt function which was called to generate the
            /// ciphertext.
            /// </remarks>
            public static string Decrypt(string cipherText,
                string passPhrase,
                string saltValue,
                string hashAlgorithm,
                int passwordIterations,
                string initVector,
                int keySize)
            {
                // Convert strings defining encryption key characteristics into byte
                // arrays. Let us assume that strings only contain ASCII codes.
                // If strings include Unicode characters, use Unicode, UTF7, or UTF8
                // encoding.
                byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
                byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

                // Convert our ciphertext into a byte array.
                byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

                // First, we must create a password, from which the key will be 
                // derived. This password will be generated from the specified 
                // passphrase and salt value. The password will be created using
                // the specified hash algorithm. Password creation can be done in
                // several iterations.
                PasswordDeriveBytes password = new PasswordDeriveBytes(
                    passPhrase,
                    saltValueBytes,
                    hashAlgorithm,
                    passwordIterations);

                // Use the password to generate pseudo-random bytes for the encryption
                // key. Specify the size of the key in bytes (instead of bits).
                byte[] keyBytes = password.GetBytes(keySize / 8);

                // Create uninitialized Rijndael encryption object.
                RijndaelManaged symmetricKey = new RijndaelManaged();

                // It is reasonable to set encryption mode to Cipher Block Chaining
                // (CBC). Use default options for other symmetric key parameters.
                symmetricKey.Mode = CipherMode.CBC;

                // Generate decryptor from the existing key bytes and initialization 
                // vector. Key size will be defined based on the number of the key 
                // bytes.
                ICryptoTransform decryptor = symmetricKey.CreateDecryptor(
                    keyBytes,
                    initVectorBytes);

                // Define memory stream which will be used to hold encrypted data.
                MemoryStream memoryStream = new MemoryStream(cipherTextBytes);

                // Define cryptographic stream (always use Read mode for encryption).
                CryptoStream cryptoStream = new CryptoStream(memoryStream,
                    decryptor,
                    CryptoStreamMode.Read);

                // Since at this point we don't know what the size of decrypted data
                // will be, allocate the buffer long enough to hold ciphertext;
                // plaintext is never longer than ciphertext.
                byte[] plainTextBytes = new byte[cipherTextBytes.Length];

                // Start decrypting.
                int decryptedByteCount = cryptoStream.Read(plainTextBytes,
                    0,
                    plainTextBytes.Length);

                // Close both streams.
                memoryStream.Close();
                cryptoStream.Close();

                // Convert decrypted data into a string. 
                // Let us assume that the original plaintext string was UTF8-encoded.
                string plainText = Encoding.UTF8.GetString(plainTextBytes,
                    0,
                    decryptedByteCount);

                // Return decrypted string.   
                return plainText;
            }
        }
    }
}
